﻿using System.Collections;
using System.Collections.Generic;
using System.Data.SqlTypes;
using System.Security.Principal;
using ActiveDirectoryUtilities.DirectoryServiceAccess;
using Microsoft.SqlServer.Server;
using System;
using System.Diagnostics;
using System.DirectoryServices.Protocols;
using AccessActiveDirectory;

public partial class UserDefinedFunctions
{
	[SqlFunction(FillRowMethodName = "Fill_fn_Forest", DataAccess = DataAccessKind.Read
		, TableDefinition =
		"ForestFqdn NVARCHAR(MAX)"
		+ ", DistinguishedName NVARCHAR(MAX)"
		+ ", ForestModeStringTranslation NVARCHAR(MAX)"
		+ ", ForestModeRawValue INT"
		)]
	public static IEnumerable fn_Forest(SqlString fqdnDomainName)
	{
		EventLogTraceListener eltl = new EventLogTraceListener("Application");
		if (ConfiguredSettings.TraceLevel >= 4)
		{
			Trace.Listeners.Add(eltl);
		}

		WindowsImpersonationContext executionContext = null;
		try
		{
			WindowsIdentity callerId = SqlContext.WindowsIdentity;
			if (callerId != null)
			{
				executionContext = callerId.Impersonate();


				string internalDomainFqdn = string.Empty;
				if (!fqdnDomainName.IsNull)
				{
					internalDomainFqdn = (string)fqdnDomainName;
				}

				Forest f = null;
				try
				{
					if (string.IsNullOrEmpty(internalDomainFqdn))
					{
						f = Forest.GetCurrentForest();
					}
					else
					{
						f = Forest.GetForest(internalDomainFqdn);
					}
			
					return new enumerateForests(f);
				}
				catch (LdapException ex)
				{
					if (ex.ErrorCode == 81)
					{
						throw new ApplicationException("Couldn't contact a DC for " + internalDomainFqdn + ".  Ensure DNS 'A' records for the forest root domain are properly registered.");
					}
					else
					{
						SharedLogic.LogTraceInfo(eltl, ex, TraceLevel.Error);
						throw ex;
					}
				}
				catch (Exception ex)
				{
					SharedLogic.LogTraceInfo(eltl, ex, TraceLevel.Error);
					throw ex;
				}

				throw new ApplicationException("Invalid codepath traversed.  Caller should never make it here.");
			}
		}
		finally
		{
			if (Trace.Listeners.Contains(eltl))
			{
				Trace.Listeners.Remove(eltl);
			}
			if (executionContext != null)
			{
				executionContext.Undo();
			}
		}
		return null;
	}

	private static void Fill_fn_Forest(
		object o
		, out SqlString forestFqdn
		, out SqlString distinguishedName
		, out SqlString forestModeStringTranslation
		, out SqlInt32 forestModeRawValue)
	{
		Forest f = (Forest)o;
		forestFqdn = f.ForestFqdn;
		distinguishedName = f.ForestDn;
		forestModeStringTranslation = f.ForestFunctionalLevel.ToString().Replace('_', ' ');
		forestModeRawValue = (int)f.ForestFunctionalLevel;
	}

	private class enumerateForests : IEnumerable
	{
		List<Forest> _forests = new List<Forest>();

		public enumerateForests(Forest f)
		{
			_forests.Add(f);
		}

		public List<Forest> Forests { get { return _forests; } }

		#region IEnumerable Members

		public IEnumerator GetEnumerator()
		{
			return _forests.GetEnumerator();
		}

		#endregion
	}
};

